#include <iostream>
#include <memory>
#include "Socket.hpp"
#include "Epoller.hpp"

static const uint16_t defaultport = 8888;

class EpollServer
{
public:
    EpollServer(uint16_t port = defaultport)
        : _port(port), _listensockfd_ptr(new Sock()), _epoller_ptr(new Epoller())
    {
    }

    void Init()
    {
        _listensockfd_ptr->Socket();
        _listensockfd_ptr->Bind(_port);
        _listensockfd_ptr->Listen();

        lg(INFO, "create listen sockfd success: %d", _listensockfd_ptr->Getfd());
    }

    void Accepter()
    {   
        uint16_t clientport = 0;
        std::string clientip;
        int sock = _listensockfd_ptr->Accept(clientip, clientport);
        if(sock > 0)
        {
            _epoller_ptr->EpollerUpdate(EPOLL_CTL_ADD, sock, EPOLLIN);
            lg(INFO, "get a new link, client info@ %s:%d", clientip, clientport);
        }
    }

    void recver(int fd)
    {
        char buffer[1024];
        int n = recv(fd, buffer, sizeof(buffer) - 1, 0);
        if(n > 0)
        {
            buffer[n] = 0;
            std::string echo_str = "Server echo $ ";
            echo_str += buffer;
            std::cout << "get a message" << echo_str.c_str() << std::endl; 
        }
        else if(n == 0)
        {
            lg(INFO, "client quit, close fd: %d", fd);
            //注意：需要先在epoll模型中删除掉fd，在进行关闭，因为被epfd管理的fd，首先它需要是有效的
            _epoller_ptr->EpollerUpdate(EPOLL_CTL_DEL, fd, 0);
            close(fd);
        }
        else
        {
            lg(ERROR, "recv error, fd is: %d", fd);
            _epoller_ptr->EpollerUpdate(EPOLL_CTL_DEL, fd, 0);
            close(fd);
        }
    }

    void Dispatcher(struct epoll_event res[], int num)
    {
        for (int i = 0; i < num; i++)
        {
            uint32_t event = res[i].events;
            int fd = res[i].data.fd;

            if (event & EPOLLIN)
            {
                if (fd == _listensockfd_ptr->Getfd())
                {
                    Accepter();
                }
                else
                {
                    recver(fd);
                }
            }
            else if(event & EPOLLOUT)
            {
                //...   
            }
        }
    }

    void start()
    {
        // 将监听套接字放到epoll模型中（插入红黑树中），监听读事件(等待新连接到来)
        _epoller_ptr->EpollerUpdate(EPOLL_CTL_ADD, _listensockfd_ptr->Getfd(), EPOLLIN);

        // 就绪队列中就绪事件需要拿到上层，存放就绪事件的数组，数组大小适当即可
        // 例如num = 64 ,但是就绪队列中有80个就绪队列也没事, 在就绪队列中剩余的就绪事件会等你下一次来获取并处理
        struct epoll_event ev[num];
        for (;;)
        {
            int n = _epoller_ptr->EpollerWait(ev, num);
            if (n > 0)
            {
                // 有事件就绪，并发处理 ev[0].data.fd 绝对是有数据的，因为我们使用EpollerWai等过了，并且返回值n>0
                lg(DEBUG, "event happened, fd is : %d", ev[0].data.fd);
                Dispatcher(ev, n);
            }
            else if (n == 0)
            {
                // 超时
                lg(INFO, "timeout...");
            }
            else
            {
                lg(ERROR, "epoll wait error");
            }
        }
    }

    ~EpollServer()
    {
        _listensockfd_ptr->Close();
    }
private:
    std::shared_ptr<Sock> _listensockfd_ptr; // 监听套接字（智能指针管理自动释放）
    std::shared_ptr<Epoller> _epoller_ptr;   // epoll实例（智能指针管理）
    uint16_t _port;                          // 服务端端口号
    static int num;                          // 一次最多处理的事件数
};

int EpollServer::num = 64;